/*
 * GrupoReceptor.cpp
 *
 *  Created on: Feb 24, 2010
 *      Author: jgrande
 */

#include "Juego.h"

using middleware::common::T_ID_JUEGO;
using middleware::common::T_ID_JUGADOR;
using middleware::common::Cantidad;

namespace aplicacion {
namespace directorio {

Juego::Juego(T_ID_JUEGO id, Cantidad min, Cantidad max, string descripcion,
		Universo* universo):
	id(id),
	universo(universo),
	nextJugadorId(1),
	min(min),
	max(max),
	descripcion(descripcion),
	anillo(NULL),
	abierto(true),
	cantidadListos(0),
	iniciado(false) {

}

Juego::~Juego() {
	if(anillo) {
		delete anillo;
	}
}

T_ID_JUGADOR Juego::suscribir(Direccion dir) {
	if(jugadores.size()==max) {
		return -1;
	}

	Jugador jugador(nextJugadorId++, dir);
	jugadores.push_back(jugador);

	return jugador.getId();
}

bool_t Juego::desuscribir(T_ID_JUGADOR idJugador) {
	if(jugadores.size()==0) {
		return FALSE;
	}

	list<Jugador>::iterator it = jugadores.begin();
	for(;it!=jugadores.end();it++) {
		if(it->getId()==idJugador) {
			if(anillo) {
				anillo->quitarReceptor(it->getDir());
			}
			jugadores.erase(it);
			return TRUE;
		}
	}

	return FALSE;
}

bool Juego::jugadorListo(T_ID_JUGADOR idJugador) {
	list<Jugador>::iterator it = jugadores.begin();
	for(;it!=jugadores.end();it++) {
		if(it->getId()==idJugador) {
			it->setListo(true);
			return true;
		}
	}
	
	return false;
}

bool Juego::isListo() const {
	if(jugadores.size()<min) {
		return false;
	}

	list<Jugador>::const_iterator it = jugadores.begin();
	for(;it!=jugadores.end();it++) {
		if(!it->isListo()) {
			return false;
		}
	}
	return true;
}

bool Juego::isIniciado() const {
	return iniciado;
}

bool Juego::iniciar() {
	logger << LogType::LOG_DEBUG_1 << "iniciando juego " << getId() << logger.endl;
	if(crearAnillo() && informarJugadores() && informarPlanetas()) {
		iniciado = true;
	}
	return iniciado;
}

bool Juego::crearAnillo() {
	if(anillo) {
		logger << LogType::LOG_ERROR << "\terror: se intentó iniciar un juego ya iniciado" << logger.endl;
		return false;
	}

	logger << LogType::LOG_DEBUG_1 << "\t* creando anillo..." << logger.endl;

	list<Direccion> emisores;
	list<Direccion> receptores;

	list<Jugador>::iterator itJugador;
	for(itJugador=jugadores.begin();itJugador!=jugadores.end();itJugador++) {
		receptores.push_back(itJugador->getDir());
	}

	const list<Planeta>& planetas = universo->getPlanetas();
	list<Planeta>::const_iterator itPlaneta;
	for(itPlaneta=planetas.begin();itPlaneta!=planetas.end();itPlaneta++) {
		emisores.push_back(itPlaneta->getDir());
	}

	anillo = new middleware::directorio::Anillo(emisores, receptores);

	return anillo;
}

bool Juego::informarJugadores() {
	logger << LogType::LOG_DEBUG_1 << "\t* informando jugadores..." << logger.endl;

	MiddlewareCliente& mw = MiddlewareClienteSingleton::instance();

	Encabezado encabezado;
	encabezado.setTipo(INICIO_JUEGO);
	encabezado.setOrigenDirectorio();

	DatosInicioJuego *datosInicioJuego = new DatosInicioJuego();
	datosInicioJuego->_idJuego = getId();

	MensajeAplicacion msj(encabezado, datosInicioJuego);

	list<Jugador>::iterator itJugador;
	for(itJugador=jugadores.begin();itJugador!=jugadores.end();itJugador++) {
		logger << LogType::LOG_DEBUG_1 << "\t* informando jugador " << itJugador->getId() << logger.endl;

		IdJugador idDestino(itJugador->getId(), getId());
		encabezado.setDestino(idDestino);

		mw.send(msj, itJugador->getDir());
	}

	return true;
}

bool Juego::informarPlanetas() {
	logger << LogType::LOG_DEBUG_1 << "\t* informando planetas..." << logger.endl;

	MiddlewareCliente& mw = MiddlewareClienteSingleton::instance();

	IdJugador idJugadorNulo;
	idJugadorNulo.setNinguno();

	list<Jugador>::const_iterator itJugador = jugadores.begin();
	list<Planeta>::const_iterator itPlaneta = universo->getPlanetas().begin();
	for(;itPlaneta!=universo->getPlanetas().end();itPlaneta++) {

		// le informa al planeta el inicio del juego
		IdPlaneta idPlaneta(itPlaneta->getId(), universo->getId());

		Encabezado encabezado;
		encabezado.setTipo(INICIO_JUEGO);
		encabezado.setOrigenDirectorio();
		encabezado.setDestino(idPlaneta);

		DatosInicioJuego *datosInicioJuego = new DatosInicioJuego();
		datosInicioJuego->_idJuego = getId();

		MensajeAplicacion msjInicioJuego(encabezado, datosInicioJuego);

		logger << LogType::LOG_DEBUG_1 << "\t* informando planeta " << itPlaneta->getId() << logger.endl;
		mw.send(msjInicioJuego, itPlaneta->getDir());

		// actualiza el dueño del planeta
		// al i-ésimo planeta de la lista se le asigna el i-ésimo jugador
		// de la lista de jugadores. los últimos planetas se van a quedar
		// sin dueño
		if(itJugador!=jugadores.end()) {
			encabezado.setTipo(CAMBIO_DUENO);

			IdJugador idDuenio(itJugador->getId(), getId());
			itJugador++;

			DatosCambiaDuenio *datosCambiaDuenio = new DatosCambiaDuenio();
			datosCambiaDuenio->_duenioAnterior = idJugadorNulo;
			datosCambiaDuenio->_duenioNuevo = idDuenio;

			MensajeAplicacion msjCambiaDuenio = MensajeAplicacion(encabezado,
					datosCambiaDuenio);

			logger << LogType::LOG_DEBUG_1 << "\t* actualizando dueño de planeta " << itPlaneta->getId() << logger.endl;
			mw.send(msjCambiaDuenio, itPlaneta->getDir());
		}
	}

	return true;
}

std::ostream& operator<<(std::ostream& os, const Juego& j) {
	os << "Juego (id=" << j.getId() <<
			", min=" << j.getMin() <<
			", max=" << j.getMax() <<
			", " << *j.getUniverso() << ")";
	return os;
}

}
}

